home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / SNIP0492.ARJ / GREP.C < prev    next >
C/C++ Source or Header  |  1991-09-30  |  20KB  |  678 lines

  1. /* The  information  in  this  document  is  subject  to  change
  2.  * without  notice  and  should not be construed as a commitment
  3.  * by Digital Equipment Corporation or by DECUS.
  4.  *
  5.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  6.  * assume any responsibility for the use or reliability of  this
  7.  * document or the described software.
  8.  *
  9.  *      Copyright (C) 1980, DECUS
  10.  *
  11.  * General permission to copy or modify, but not for profit,  is
  12.  * hereby  granted,  provided that the above copyright notice is
  13.  * included and reference made to  the  fact  that  reproduction
  14.  * privileges were granted by DECUS.
  15.  */
  16.  
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <dos.h>
  20.  
  21. void file(char *s);
  22. void cant(char *s);
  23. void help(char *hp);
  24. void usage(char *s);
  25. void compile(char *source);
  26. void store(int op);
  27. void badpat(char *message, char *source, char *stop);
  28. void grep(FILE *fp, char *fn);
  29. void error(char *s);
  30.  
  31. /* grep.
  32.  *
  33.  * Runs on the Decus compiler or on vms.
  34.  * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
  35.  *
  36.  * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
  37.  *
  38.  * Converted to ANSI-style C (Zortech C++) compiler by Bob Jarvis
  39.  *
  40.  * On vms, define as:
  41.  *
  42.  *      grep :== "$disk:[account]grep"          (native)
  43.  *      grep :== "$disk:[account]grep grep"     (Decus)
  44.  *
  45.  * See below for more information.
  46.  */
  47.  
  48.  
  49. char    *documentation =
  50.    "grep searches a file for a given pattern.  Execute by\n"
  51.    "   grep [flags] regular_expression file_list\n"
  52.    "\n"
  53.    "Flags are single characters preceeded by '-':\n"
  54.    "   -c      Only a count of matching lines is printed\n"
  55.    "   -f      Print file name for matching lines switch, see below\n"
  56.    "   -n      Each line is preceeded by its line number\n"
  57.    "   -v      Only print non-matching lines\n"
  58.    "\n"
  59.    "The file_list is a list of files (wildcards are acceptable on RSX modes).\n"
  60.    "\n"
  61.    "The file name is normally printed if there is a file given.\n"
  62.    "The -f flag reverses this action (print name no file, not if more).";
  63.  
  64.  
  65. char    *patdoc =
  66.    "The regular_expression defines the pattern to search for.  Upper- and\n"
  67.    "lower-case are always ignored.  Blank lines never match.  The expression\n"
  68.    "should be quoted to prevent file-name translation.\n"
  69.    "x      An ordinary character (not mentioned below) matches that character.\n"
  70.    "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.\n"
  71.    "'^'    A circumflex at the beginning of an expression matches the\n"
  72.    "       beginning of a line.\n"
  73.    "'$'    A dollar-sign at the end of an expression matches the end of a line.\n"
  74.    "'.'    A period matches any character except \"new-line\".\n"
  75.    "':a'   A colon matches a class of characters described by the following\n"
  76.    "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,\n"
  77.    "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and\n"
  78.    "': '     other control characters, such as new-line.\n"
  79.    "'*'    An expression followed by an asterisk matches zero or more\n"
  80.    "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"\n"
  81.    "       \"foo\", etc.\n"
  82.    "'+'    An expression followed by a plus sign matches one or more\n"
  83.    "       occurrances of that expression: \"fo+\" matches \"fo\", etc.\n"
  84.    "'-'    An expression followed by a minus sign optionally matches\n"
  85.    "       the expression.\n"
  86.    "'[]'   A string enclosed in square brackets matches any character in\n"
  87.    "       that string, but no others.  If the first character in the\n"
  88.    "       string is a circumflex, the expression matches any character\n"
  89.    "       except \"new-line\" and the characters in the string.  For\n"
  90.    "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"\n"
  91.    "       matches \"abc\" but not \"axb\".  A range of characters may be\n"
  92.    "       specified by two characters separated by \"-\".  Note that\n"
  93.    "       [a-z] matches alphabetics, while [z-a] never matches.\n"
  94.    "The concatenation of regular expressions is a regular expression.";
  95.  
  96. #define LMAX    512
  97. #define PMAX    256
  98.  
  99. #define CHAR    1
  100. #define BOL     2
  101. #define EOL     3
  102. #define ANY     4
  103. #define CLASS   5
  104. #define NCLASS  6
  105. #define STAR    7
  106. #define PLUS    8
  107. #define MINUS   9
  108. #define ALPHA   10
  109. #define DIGIT   11
  110. #define NALPHA  12
  111. #define PUNCT   13
  112. #define RANGE   14
  113. #define ENDPAT  15
  114.  
  115. int     cflag;
  116. int     fflag;
  117. int     nflag;
  118. int     vflag;
  119. int     nfile;
  120.  
  121. int     debug   =       0;         /* Set for debug code      */
  122.  
  123. char    *pp;
  124.  
  125. #ifndef vms
  126. char    file_name[81];
  127. #endif
  128.  
  129. char    lbuf[LMAX];
  130. char    pbuf[PMAX];
  131.  
  132. /*******************************************************/
  133.  
  134. int main(int argc, char *argv[])
  135. {
  136.       register char   *p;
  137.       register int    c, i;
  138.       int             gotpattern;
  139.       int             gotcha;
  140.       FILE            *f;
  141.       struct FIND     *sfptr;
  142.  
  143.       if(argc <= 1)
  144.             usage("No arguments");
  145.  
  146.       if(argc == 2 && argv[1][0] == '?' && argv[1][1] == 0)
  147.       {
  148.             help(documentation);
  149.             help(patdoc);
  150.             return;
  151.       }
  152.  
  153.       nfile = argc-1;
  154.       gotpattern = 0;
  155.  
  156.       for(i = 1 ; i < argc ; ++i)
  157.       {
  158.             p = argv[i];
  159.             if(*p == '-')
  160.             {
  161.                   ++p;
  162.                   while((c = *p++) != 0)
  163.                   {
  164.                         switch(tolower(c))
  165.                         {
  166.                         case '?':
  167.                               help(documentation);
  168.                               break;
  169.  
  170.                         case 'C':
  171.                         case 'c':
  172.                               ++cflag;
  173.                               break;
  174.  
  175.                         case 'D':
  176.                         case 'd':
  177.                               ++debug;
  178.                               break;
  179.  
  180.                         case 'F':
  181.                         case 'f':
  182.                               ++fflag;
  183.                               break;
  184.  
  185.                         case 'n':
  186.                         case 'N':
  187.                               ++nflag;
  188.                               break;
  189.  
  190.                         case 'v':
  191.                         case 'V':
  192.                               ++vflag;
  193.                               break;
  194.  
  195.                         default:
  196.                               usage("Unknown flag");
  197.                         }
  198.                   }
  199.                   argv[i] = 0;
  200.                   --nfile;
  201.             } 
  202.             else if(!gotpattern)
  203.             {
  204.                   compile(p);
  205.                   argv[i] = 0;
  206.                   ++gotpattern;
  207.                   --nfile;
  208.             }
  209.       }
  210.  
  211.       if(!gotpattern)
  212.             usage("No pattern");
  213.  
  214.       if(nfile == 0)
  215.             grep(stdin, 0);
  216.       else
  217.       {
  218.             fflag = fflag ^ (nfile > 0);
  219.             for(i = 1 ; i < argc ; ++i)
  220.             {
  221.                   if((p = argv[i]) != NULL)
  222.                   {
  223.                         sfptr = findfirst(p, FA_NORMAL);
  224.  
  225.                         while(sfptr != NULL)
  226.                         {
  227.                               if((f=fopen(sfptr->name, "r")) == NULL)
  228.                                     cant(sfptr->name);
  229.                               else
  230.                               {
  231.                                     grep(f, sfptr->name);
  232.                                     fclose(f);
  233.                               }
  234.  
  235.                               sfptr = findnext();
  236.                         }
  237.                   }
  238.             }
  239.       }
  240. }
  241.  
  242. /*******************************************************/
  243.  
  244. void file(char *s)
  245. {
  246.       printf("File %s:\n", s);
  247. }
  248.  
  249. /*******************************************************/
  250.  
  251. void cant(char *s)
  252. {
  253.       fprintf(stderr, "%s: cannot open\n", s);
  254. }
  255.  
  256.  
  257. /*******************************************************/
  258.  
  259. void help(char *hp)
  260. {
  261.       fputs(hp, stderr);
  262. }
  263.  
  264.  
  265. /*******************************************************/
  266.  
  267. void usage(char *s)
  268. {
  269.       fprintf(stderr, "?GREP-E-%s\n", s);
  270.       fprintf(stderr,
  271.             "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  272.       exit(1);
  273. }
  274.  
  275. /*******************************************************/
  276.  
  277. void compile(char *source)   /* Pattern to compile         */
  278. /*
  279.  * Compile the pattern into global pbuf[]
  280.  */
  281. {
  282.       register char  *s;         /* Source string pointer     */
  283.       register char  *lp;        /* Last pattern pointer      */
  284.       register int   c;          /* Current character         */
  285.       int            o;          /* Temp                      */
  286.       char           *spp;       /* Save beginning of pattern */
  287.       char           *cclass();  /* Compile class routine     */
  288.  
  289.       s = source;
  290.       if(debug)
  291.             printf("Pattern = \"%s\"\n", s);
  292.       pp = pbuf;
  293.       while((c = *s++) != 0)
  294.       {
  295.       /*
  296.        * STAR, PLUS and MINUS are special.
  297.        */
  298.             if(c == '*' || c == '+' || c == '-')
  299.             {
  300.                   if(pp == pbuf || (o=pp[-1]) == BOL || o == EOL ||
  301.                         o == STAR || o == PLUS || o == MINUS)
  302.                   {
  303.                         badpat("Illegal occurrance op.", source, s);
  304.                   }
  305.  
  306.                   store(ENDPAT);
  307.                   store(ENDPAT);
  308.                   spp = pp;               /* Save pattern end     */
  309.                   while(--pp > lp)        /* Move pattern down    */
  310.                         *pp = pp[-1];     /* one byte             */
  311.                   *pp =   (c == '*') ? STAR : (c == '-') ? MINUS : PLUS;
  312.                   pp = spp;               /* Restore pattern end  */
  313.                   continue;
  314.             }
  315.  
  316.       /*
  317.        * All the rest.
  318.        */
  319.  
  320.             lp = pp;         /* Remember start       */
  321.             switch(c)
  322.             {
  323.             case '^':
  324.                   store(BOL);
  325.                   break;
  326.  
  327.             case '$':
  328.                   store(EOL);
  329.                   break;
  330.  
  331.             case '.':
  332.                   store(ANY);
  333.                   break;
  334.  
  335.             case '[':
  336.                   s = cclass(source, s);
  337.                   break;
  338.  
  339.             case ':':
  340.                   if(*s)
  341.                   {
  342.                         c = *s++;
  343.                         switch(tolower(c))
  344.                         {
  345.                         case 'a':
  346.                         case 'A':
  347.                               store(ALPHA);
  348.                               break;
  349.  
  350.                         case 'd':
  351.                         case 'D':
  352.                               store(DIGIT);
  353.                               break;
  354.  
  355.                         case 'n':
  356.                         case 'N':
  357.                               store(NALPHA);
  358.                               break;
  359.  
  360.                         case ' ':
  361.                               store(PUNCT);
  362.                               break;
  363.  
  364.                         default:
  365.                               badpat("Unknown : type", source, s);
  366.                         }
  367.  
  368.                         break;
  369.                   }
  370.                   else  badpat("No : type", source, s);
  371.  
  372.             case '\\':
  373.                   if(*s)
  374.                         c = *s++;
  375.  
  376.             default:
  377.                   store(CHAR);
  378.                   store(tolower(c));
  379.             }
  380.       }
  381.  
  382.       store(ENDPAT);
  383.       store(0);                /* Terminate string     */
  384.  
  385.       if(debug)
  386.       {
  387.             for (lp = pbuf; lp < pp;)
  388.             {
  389.                   if((c = (*lp++ & 0377)) < ' ')
  390.                         printf("\\%o ", c);
  391.                   else  printf("%c ", c);
  392.             }
  393.  
  394.             printf("\n");
  395.       }
  396. }
  397.  
  398. /*******************************************************/
  399.  
  400. char *cclass(char *source, char *src)
  401. /*
  402.  * Compile a class (within [])
  403.  */
  404. {
  405.       register char   *s;        /* Source pointer    */
  406.       register char   *cp;       /* Pattern start     */
  407.       register int    c;         /* Current character */
  408.       int             o;         /* Temp              */
  409.  
  410.       s = src;
  411.       o = CLASS;
  412.       if(*s == '^')
  413.       {
  414.             ++s;
  415.             o = NCLASS;
  416.       }
  417.  
  418.       store(o);
  419.       cp = pp;
  420.       store(0);                               /* Byte count      */
  421.       while( ((c = *s++) != 0) && c != ']')
  422.       {
  423.             if(c == '\\')                     /* Store quoted char    */
  424.             {
  425.                   if((c = *s++) == '\0')      /* Gotta get something  */
  426.                         badpat("Class terminates badly", source, s);
  427.                   else  store(tolower(c));
  428.             }
  429.             else if(c == '-' && (pp - cp) > 1 && *s != ']' && *s != '\0')
  430.             {
  431.                   c = pp[-1];             /* Range start     */
  432.                   pp[-1] = RANGE;         /* Range signal    */
  433.                   store(c);               /* Re-store start  */
  434.                   c = *s++;               /* Get end char and*/
  435.                   store(tolower(c));      /* Store it        */
  436.             }
  437.             else
  438.             {
  439.                   store(tolower(c));      /* Store normal char */
  440.             }
  441.       }
  442.  
  443.       if(c != ']')
  444.             badpat("Unterminated class", source, s);
  445.  
  446.       if((c = (pp - cp)) >= 256)
  447.             badpat("Class too large", source, s);
  448.  
  449.       if(c == 0)
  450.             badpat("Empty class", source, s);
  451.  
  452.       *cp = c;
  453.       return(s);
  454. }
  455.  
  456. /*******************************************************/
  457.  
  458. void store(int op)
  459. {
  460.       if(pp >= &pbuf[PMAX])
  461.             error("Pattern too complex\n");
  462.       *pp++ = op;
  463. }
  464.  
  465.  
  466. /*******************************************************/
  467.  
  468. void badpat(char *message, char *source, char *stop)
  469. {
  470.       register int    c;
  471.  
  472.       fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  473.       fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  474.             stop-source, stop[-1]);
  475.       error("?GREP-E-Bad pattern\n");
  476. }
  477.  
  478.  
  479.  
  480. /*******************************************************/
  481.  
  482. void grep(FILE *fp, char *fn)
  483. /*
  484.  * Scan the file for the pattern in pbuf[]
  485.  */
  486. {
  487.       register int lno, count, m;
  488.  
  489.       lno = 0;
  490.       count = 0;
  491.       while(fgets(lbuf, LMAX, fp))
  492.       {
  493.             ++lno;
  494.             m = match();
  495.             if((m && !vflag) || (!m && vflag))
  496.             {
  497.                   ++count;
  498.                   if(!cflag)
  499.                   {
  500.                         if(fflag && fn)
  501.                         {
  502.                               file(fn);
  503.                               fn = 0;
  504.                         }
  505.                         if(nflag)
  506.                               printf("%d\t", lno);
  507.  
  508.                         printf("%s\n", lbuf);
  509.                   }
  510.             }
  511.       }
  512.  
  513.       if(cflag)
  514.       {
  515.             if(fflag && fn)
  516.                   file(fn);
  517.  
  518.             printf("%d\n", count);
  519.       }
  520. }
  521.  
  522. /*******************************************************/
  523.  
  524. int match(void)
  525. /*
  526.  * Match the current line (in lbuf[]), return 1 if it does.
  527.  */
  528. {
  529.       register char   *l;        /* Line pointer       */
  530.       char *pmatch();
  531.  
  532.       for (l = lbuf; *l; l++)
  533.       {
  534.             if(pmatch(l, pbuf))
  535.                   return(1);
  536.       }
  537.  
  538.       return(0);
  539. }
  540.  
  541. /*******************************************************/
  542.  
  543. char *pmatch(char *line, char *pattern)
  544. {
  545.       register char   *l;        /* Current line pointer         */
  546.       register char   *p;        /* Current pattern pointer      */
  547.       register char   c;         /* Current character            */
  548.       char            *e;        /* End for STAR and PLUS match  */
  549.       int             op;        /* Pattern operation            */
  550.       int             n;         /* Class counter                */
  551.       char            *are;      /* Start of STAR match          */
  552.  
  553.       l = line;
  554.       if(debug > 1)
  555.             printf("pmatch(\"%s\")\n", line);
  556.       p = pattern;
  557.       while((op = *p++) != ENDPAT)
  558.       {
  559.             if(debug > 1)
  560.                   printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  561.                         l-line, *l, *l, op);
  562.             switch(op)
  563.             {
  564.             case CHAR:
  565.                   if(tolower(*l++) != *p++)
  566.                         return(NULL);
  567.                   break;
  568.  
  569.             case BOL:
  570.                   if(l != lbuf)
  571.                         return(NULL);
  572.                   break;
  573.  
  574.             case EOL:
  575.                   if(*l != '\0')
  576.                         return(NULL);
  577.                   break;
  578.  
  579.             case ANY:
  580.                   if(*l++ == '\0')
  581.                         return(NULL);
  582.                   break;
  583.  
  584.             case DIGIT:
  585.                   if((c = *l++) < '0' || (c > '9'))
  586.                         return(NULL);
  587.                   break;
  588.  
  589.             case ALPHA:
  590.                   c = tolower(*l++);
  591.                   if(c < 'a' || c > 'z')
  592.                         return(NULL);
  593.                   break;
  594.  
  595.             case NALPHA:
  596.                   c = tolower(*l++);
  597.                   if(c >= 'a' && c <= 'z')
  598.                         break;
  599.                   else if(c < '0' || c > '9')
  600.                         return(NULL);
  601.                   break;
  602.  
  603.             case PUNCT:
  604.                   c = *l++;
  605.                   if(c == 0 || c > ' ')
  606.                         return(NULL);
  607.                   break;
  608.  
  609.             case CLASS:
  610.             case NCLASS:
  611.                   c = tolower(*l++);
  612.                   n = *p++ & 0377;
  613.                   do
  614.                   {
  615.                         if(*p == RANGE)
  616.                         {
  617.                               p += 3;
  618.                               n -= 2;
  619.                               if(c >= p[-2] && c <= p[-1])
  620.                                     break;
  621.                         }
  622.                         else if(c == *p++)
  623.                               break;
  624.                   } while(--n > 1);
  625.  
  626.                   if((op == CLASS) == (n <= 1))
  627.                         return(0);
  628.  
  629.                   if(op == CLASS)
  630.                         p += n - 2;
  631.                   break;
  632.  
  633.             case MINUS:
  634.                   e = pmatch(l, p);       /* Look for a match    */
  635.                   while(*p++ != ENDPAT)   /* Skip over pattern   */
  636.                         ;                /* null statement to avoid warning */
  637.                   if(e)                   /* Got a match?        */
  638.                         l = e;            /* Yes, update string  */
  639.                   break;                  /* Always succeeds     */
  640.  
  641.             case PLUS:                    /* One or more ...     */
  642.                   if((l = pmatch(l, p)) == 0)
  643.                         return(NULL);     /* Gotta have a match  */
  644.  
  645.             case STAR:                    /* Zero or more ...    */
  646.                   are = l;                /* Remember line start */
  647.                   while(*l && ((e = pmatch(l, p)) != NULL) )
  648.                         l = e;            /* Get longest match   */
  649.  
  650.                   while(*p++ != ENDPAT)   /* Skip over pattern   */
  651.                         ;                /* null statement to avoid warning */
  652.  
  653.                   while(l >= are)         /* Try to match rest   */
  654.                   {
  655.                         if((e = pmatch(l, p)) != 0)
  656.                               return(e);
  657.                         --l;              /* Nope, try earlier   */
  658.                   }
  659.  
  660.                   return(NULL);           /* Nothing else worked */
  661.  
  662.             default:
  663.                   printf("Bad op code %d\n", op);
  664.                   error("Cannot happen -- match\n");
  665.             }
  666.       }
  667.  
  668.       return(l);
  669. }
  670.  
  671. /*******************************************************/
  672.  
  673. void error(char *s)
  674. {
  675.       fprintf(stderr, "%s", s);
  676.       exit(1);
  677. }
  678.